using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Wintellect.PowerCollections;
using System.Threading;
using System.ComponentModel;

namespace NHTrace
{
  public class NHTraceManager
  {
    public NHTraceManager(int messagesRefreshTimeInMilliseconds,
      ReceiverFactory receiverFactory,
      LoggerProvider loggerProvider)
    {
      _loggerProvider = loggerProvider;
      _receiverFactory = receiverFactory;

      _timer = new System.Timers.Timer();
      _timer.Interval = messagesRefreshTimeInMilliseconds;
      _timer.Elapsed += timer_Elapsed;
      _timer.Start();
    }

    private System.Timers.Timer _timer;
    private bool _messagesChanged = false;

    void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
      lock (thisLock)
      {
        if (_messagesChanged)
        {
          IList<string> messages = GetMessages();
          renderMessages(messages);
          _messagesChanged = false;
        }
      }
    }

    private bool _logEnabled;
    public bool LogEnabled
    {
      get
      {
        return _logEnabled;
      }
      set
      {
        if (_logEnabled != value)
        {
          _logEnabled = value;

          if (_logEnabled)
          {
            _logger = _loggerProvider.GetLogger(this);
            _receiver = _receiverFactory.GetReceiver(_logger, typeof(MsmqReceiver));
            _receiver.Initialize();
            _logger.Enable();
          }
          else
          {
            if (_receiver != null)
            {
              try
              {
                _logger.Disable();
                _receiver.Terminate();
              }
              finally
              {
                _receiver = null;
              }
            }
          }
        }
      }
    }

    public bool ShowProcessName { get; set; }

    private Logger _logger;
    private IReceiver _receiver;
    private List<IMessageRenderer> _renderers = new List<IMessageRenderer>();
    private OrderedDictionary<DateTime, OrderedMultiDictionary<long, string>> _messages = 
      new OrderedDictionary<DateTime, OrderedMultiDictionary<long, string>>();
    private ReceiverFactory _receiverFactory;
    private LoggerProvider _loggerProvider;

    public void SubscribeRenderer(IMessageRenderer renderer)
    {
      if (_renderers.IndexOf(renderer) < 0)
        _renderers.Add(renderer);
    }

    public void UnsubscribeRenderer(IMessageRenderer renderer)
    {
      int index = _renderers.IndexOf(renderer);
      if (index >= 0)
        _renderers.RemoveAt(index);
    }

    private void renderMessages(IList<string> messages)
    {
      foreach (var renderer in _renderers)
        renderer.Render(messages);
    }

    private int _messagesSize;
    public int MessagesSize
    {
      get
      {
        return _messagesSize;
      }
    }

    private int _messageCount;
    public int MessageCount 
    {
      get
      {
        return _messageCount;
      }
    }

    private object thisLock = new object();

    public void AddMessage(LogMessage message)
    {
      lock (thisLock)
      {

        if (!_messages.ContainsKey(message.TimeStamp))
          _messages.Add(message.TimeStamp, new OrderedMultiDictionary<long, string>(true));

        _messages[message.TimeStamp].Add(message.Id, message.Message);

        _messagesSize += message.Message.Length;
        _messageCount += 1;
      }
    }

    public void RemoveFirstMessage()
    {
      lock (thisLock)
      {

        int sizeToRemove = _messages.ElementAt(0).Value.ElementAt(0).Value.ElementAt(0).Length;
        _messages.ElementAt(0).Value.ElementAt(0).Value.Remove(_messages.ElementAt(0).Value.ElementAt(0).Value.ElementAt(0));

        if ((_messages.ElementAt(0).Value.Count == 0) || (_messages.ElementAt(0).Value.ElementAt(0).Value.Count == 0))
          _messages.Remove(_messages.ElementAt(0).Key);

        _messagesSize -= sizeToRemove;
        _messageCount -= 1;
      }
    }

    public void WriteMessagesToWindow()
    {
      lock (thisLock)
      {
        _messagesChanged = true;
      }
    }

    public void ClearMessages()
    {
      lock (thisLock)
      {
        _messages.Clear();
        _messagesSize = 0;
        _messageCount = 0;
      }
    }

    public IList<string> GetMessages()
    {
      IList<string> messages = new List<string>();
      lock (thisLock)
      {
        foreach (var item in _messages)
        {
          foreach (var item2 in item.Value)
            foreach (var messageStr in item2.Value)
              messages.Add(messageStr);
        }
      }

      return messages;
    }
  }

}
